{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    ">原文地址 https://blog.csdn.net/qq_33733970/article/details/77719427?utm_medium=distribute.pc_relevant.none-task-blog-baidujs-1\n",
    "\n",
    "GIL 与互斥锁再理解\n",
    "===========\n",
    "\n",
    "![](https://img-blog.csdn.net/20170830151042308?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcXFfMzM3MzM5NzA=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)\n",
    "\n",
    "线程一要把 python 代码交给解释器去执行，而此时垃圾回收线程和线程二也需要将自己的任务交给 python 解释器去执行，为了防止各个线程之间的数据产生冲突，谁拿到 GIL 锁的权限谁才能执行自己的任务，这就避免了不同任务之间的数据不会产生冲突，这是在同一个进程中加 GIL 锁会保证数据的安全，不同的数据要加不同的锁。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "死锁与递归锁\n",
    "======\n",
    "\n",
    "死锁\n",
    "--\n",
    "\n",
    "### 代码演示\n",
    "\n",
    "```py\n",
    "from threading import Thread, Lock\n",
    "import time\n",
    "mutexA = Lock()\n",
    "mutexB = Lock()\n",
    "\n",
    "class MyThread(Thread):\n",
    "    def run(self):\n",
    "        self.f1()\n",
    "        self.f2()\n",
    "\n",
    "    def f1(self):\n",
    "        mutexA.acquire()\n",
    "        print('%s 拿到A锁' % self.name)\n",
    "        mutexB.acquire()\n",
    "        print('%s 拿到B锁' % self.name)\n",
    "        mutexB.release()\n",
    "        mutexA.release()\n",
    "\n",
    "    def f2(self):\n",
    "        mutexB.acquire()\n",
    "        time.sleep(1)\n",
    "        print('%s 拿到B锁' % self.name)\n",
    "        mutexA.acquire()\n",
    "        print('%s 拿到A锁' % self.name)\n",
    "        mutexA.release()\n",
    "        mutexB.release()\n",
    "\n",
    "\n",
    "if __name__ == '__main__':\n",
    "    for i in range(10):\n",
    "        t = MyThread()\n",
    "        t.start()\n",
    "```\n",
    "\n",
    "该种情况出现死锁：  \n",
    "![](https://img-blog.csdn.net/20170830153434695?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcXFfMzM3MzM5NzA=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)\n",
    "\n",
    "### 代码讲解\n",
    "\n",
    "由于 Thread-1 创建的比较快，所以 Thread-1 先抢到 A 锁，继而顺利成章的拿到 B 锁，当 Thread-1 释放掉 A 锁时，另外 9 个线程抢 A 锁，于此同时，Thread-1 抢到 B 锁，而此时 Thread-2 抢到 A 锁，这样 Thread-1、Thread-2 就等待彼此把锁释放掉，这样程序就卡住了，解决这个问题就用到了递归锁。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "递归锁\n",
    "---\n",
    "\n",
    "### 代码演示\n",
    "\n",
    "```py\n",
    "from threading import Thread, Lock, RLock\n",
    "import time\n",
    "\n",
    "# mutexA = Lock()\n",
    "# mutexB = Lock()\n",
    "mutexA = mutexB = RLock()\n",
    "\n",
    "\n",
    "class MyThread(Thread):\n",
    "    def run(self):\n",
    "        self.f1()\n",
    "        self.f2()\n",
    "\n",
    "    def f1(self):\n",
    "        mutexA.acquire()\n",
    "        print('%s 拿到A锁' % self.name)\n",
    "        mutexB.acquire()\n",
    "        print('%s 拿到B锁' % self.name)\n",
    "        mutexB.release()\n",
    "        mutexA.release()\n",
    "\n",
    "    def f2(self):\n",
    "        mutexB.acquire()\n",
    "        time.sleep(1)\n",
    "        print('%s 拿到B锁' % self.name)\n",
    "        mutexA.acquire()\n",
    "        print('%s 拿到A锁' % self.name)\n",
    "        mutexA.release()\n",
    "        mutexB.release()\n",
    "\n",
    "\n",
    "if __name__ == '__main__':\n",
    "    for i in range(10):\n",
    "        t = MyThread()\n",
    "        t.start()\n",
    "```\n",
    "\n",
    "### 代码讲解\n",
    "\n",
    "递归锁时通过计数完成对锁的控制的，当 acquire 一次，count+=1，release 一次，count-=1，当 count=0，所有的线程都可以对锁进行抢夺。从而避免了死锁的产生。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "信号量 Semaphore\n",
    "=============\n",
    "\n",
    "代码演示\n",
    "----"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```py\n",
    "from threading import Thread, Semaphore, currentThread\n",
    "import time\n",
    "smph = Semaphore(5)\n",
    "\n",
    "\n",
    "def do_task():\n",
    "    smph.acquire()\n",
    "    print('\\033[45m%s\\033[0m 获得了权限' % currentThread().name)\n",
    "    time.sleep(2)\n",
    "    print('\\033[46m%s\\033[0m 放弃了权限' % currentThread().name)\n",
    "    smph.release()\n",
    "\n",
    "\n",
    "if __name__ == '__main__':\n",
    "    for i in range(10)\n",
    "        t = Thread(target=do_task, )\n",
    "        t.start() \n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "代码效果\n",
    "----\n",
    "\n",
    "![](https://img-blog.csdn.net/20170830155911339?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcXFfMzM3MzM5NzA=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)\n",
    "\n",
    "代码讲解\n",
    "----\n",
    "\n",
    "信号量 Semaphore 本质也是一把锁，但是这把锁可以限定允许多个任务同时执行任务，但是不能超出规定的限制，下面的代码参数 5 就代表可以执行 5 个任务，如果第 6 个任务要执行，必须等 5 个任务中的一个结束，然后第六个才能进入执行。\n",
    "\n",
    "```\n",
    "smph = Semaphore(5)\n",
    "```\n",
    "\n",
    "这有点像进程池，只不过进程池规定了进程数量，多个任务进入进程池只能有数量一定的进程进行处理。，但是 Semaphore 可以产生多个线程。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "线程 Queue\n",
    "========\n",
    "\n",
    "队列 Queue\n",
    "--------\n",
    "\n",
    "### 代码演示"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1\n",
      "1\n",
      "{'a': 1}\n"
     ]
    }
   ],
   "source": [
    "import queue\n",
    "\n",
    "q = queue.Queue()\n",
    "q.put('1')\n",
    "q.put(1)\n",
    "q.put({'a': 1})\n",
    "\n",
    "print(q.get())\n",
    "print(q.get())\n",
    "print(q.get())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 代码讲解\n",
    "\n",
    "1.  先进先出\n",
    "2.  可以存放任意类型数据\n",
    "\n",
    "堆栈 Queue\n",
    "--------\n",
    "\n",
    "### 代码演示"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'a': 1}\n",
      "1\n",
      "1\n"
     ]
    }
   ],
   "source": [
    "import queue\n",
    "q = queue.LifoQueue()\n",
    "q.put(1)\n",
    "q.put('1')\n",
    "q.put({'a': 1})\n",
    "\n",
    "print(q.get())\n",
    "print(q.get())\n",
    "print(q.get())\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 代码讲解\n",
    "\n",
    "1.  可以存放任意数据类型\n",
    "2.  Lifo 代表后进先出\n",
    "\n",
    "优先级 Queue\n",
    "---------\n",
    "\n",
    "### 代码演示"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(10, 'Q')\n",
      "(20, 'A')\n",
      "(30, 'Z')\n"
     ]
    }
   ],
   "source": [
    "import queue\n",
    "\n",
    "q = queue.PriorityQueue()\n",
    "q.put((10, 'Q'))\n",
    "q.put((30, 'Z'))\n",
    "q.put((20, 'A'))\n",
    "\n",
    "print(q.get())\n",
    "print(q.get())\n",
    "print(q.get())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 代码讲解\n",
    "\n",
    "1.  存放的数据是元组类型，带有优先级数字越小优先级越高。\n",
    "2.  数据优先级高的优先被取出。\n",
    "3.  用于 VIP 用户数据优先被取出场景，因为上面两种都要挨个取出。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Event\n",
    "=====\n",
    "\n",
    "代码演示\n",
    "----\n",
    "\n",
    "```py\n",
    "from threading import Thread, Event, currentThread\n",
    "import time\n",
    "\n",
    "e = Event()\n",
    "\n",
    "\n",
    "def traffic_lights():\n",
    "    time.sleep(5)\n",
    "    e.set()\n",
    "\n",
    "\n",
    "def cars():\n",
    "    print('\\033[45m%s\\033[0m is waiting' % currentThread().name)\n",
    "    e.wait()\n",
    "    print('\\033[45m%s\\033[0m is running' % currentThread().name)\n",
    "\n",
    "\n",
    "if __name__ == '__main__':\n",
    "    for i in range(10):\n",
    "        t = Thread(target=cars, )\n",
    "        t.start()\n",
    "    traffic_lights = Thread(target=traffic_lights, )\n",
    "    traffic_lights.start()\n",
    "```\n",
    "\n",
    "### 代码讲解\n",
    "\n",
    "首先创建 10 个线程代表 10 辆车正在等信号灯，创建 1 个线程代表信号灯，当 10 辆汽车被创建后就等着信号灯发信号起跑，当遇到 e.wait() 时程序被挂起，等待信号灯变绿，而 e.set() 就是来改变这个状态让信号灯变绿，当 e.set 被设置后 cars 等到了信号，就可以继续往后跑了，代码可以继续执行了。e.set() 默认 False，e.set() 调用后值变为 True，e.wait() 接收到后程序由挂起变为可执行。\n",
    "\n",
    "### 应用场景\n",
    "\n",
    "#### 代码演示\n",
    "\n",
    "```py\n",
    "from threading import Thread, Event, currentThread\n",
    "import time\n",
    "\n",
    "e = Event()\n",
    "\n",
    "\n",
    "def check_sql():\n",
    "    print('%s is checking mySQL' % currentThread().name)\n",
    "    time.sleep(5)\n",
    "    e.set()\n",
    "\n",
    "\n",
    "def link_sql():\n",
    "    count = 1\n",
    "    while not e.is_set():#e.isSet是一个绑定方法，自带布尔值为True,e.is_set()默认值为False\n",
    "        e.wait(timeout=1)\n",
    "        print('%s is trying %s' % (currentThread().name, count))\n",
    "        if count > 3:\n",
    "            raise ConnectionError('连接超时')\n",
    "        count += 1\n",
    "    print('%s is connecting' % currentThread().name)\n",
    "\n",
    "\n",
    "if __name__ == '__main__':\n",
    "    t_check = Thread(target=check_sql, )\n",
    "    t_check.start()\n",
    "    for i in range(3):\n",
    "        t_link = Thread(target=link_sql, )\n",
    "        t_link.start()\n",
    "```\n",
    "\n",
    "#### 代码讲解\n",
    "\n",
    "1.  数据库远程连接\n",
    "2.  e.isSet 是一个绑定方法，自带布尔值为 True，e.is_set() 默认值为 False"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "定时器\n",
    "===\n",
    "\n",
    "代码演示\n",
    "----"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1590843636\n",
      "10 我被执行了~\n",
      "1590843639\n"
     ]
    }
   ],
   "source": [
    "from threading import Timer\n",
    "import time\n",
    "\n",
    "\n",
    "def deal_task(n):\n",
    "    print('%s 我被执行了~' % n)\n",
    "\n",
    "\n",
    "print(int(time.time()))\n",
    "t = Timer(3, deal_task, args=(10,))\n",
    "t.start()\n",
    "t.join()\n",
    "print(int(time.time()))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "代码讲解\n",
    "----\n",
    "\n",
    "注意传参时必须是元组形式"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.3"
  },
  "toc": {
   "base_numbering": 1,
   "nav_menu": {},
   "number_sections": true,
   "sideBar": true,
   "skip_h1_title": false,
   "title_cell": "Table of Contents",
   "title_sidebar": "Contents",
   "toc_cell": false,
   "toc_position": {},
   "toc_section_display": true,
   "toc_window_display": true
  },
  "varInspector": {
   "cols": {
    "lenName": 16,
    "lenType": 16,
    "lenVar": 40
   },
   "kernels_config": {
    "python": {
     "delete_cmd_postfix": "",
     "delete_cmd_prefix": "del ",
     "library": "var_list.py",
     "varRefreshCmd": "print(var_dic_list())"
    },
    "r": {
     "delete_cmd_postfix": ") ",
     "delete_cmd_prefix": "rm(",
     "library": "var_list.r",
     "varRefreshCmd": "cat(var_dic_list()) "
    }
   },
   "types_to_exclude": [
    "module",
    "function",
    "builtin_function_or_method",
    "instance",
    "_Feature"
   ],
   "window_display": false
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
